<template>
  <div ref="editor" class="main"></div>
</template>
<script setup lang="ts" name="MonacoEditor">
import * as monaco from "monaco-editor";
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import createSqlCompleter from "./util/sql-completion";
import createJavascriptCompleter from "./util/javascript-completion";
import registerLanguage from "./util/log-language";
import { onMounted, reactive, ref, watch } from "vue";
const props = defineProps({
	options: {
		type: Object,
		default() {
			return {};
		}
	},
	value: {
		type: String,
		required: false
	},
	language: {
		type: String
	},
	hints: {
		type: Array,
		default() {
			return [];
		}
	}
})
const editor = ref<HTMLElement>()
// 定义子组件向父组件传值/事件
const emit = defineEmits(['update:modelValue', 'change', 'onblur', 'onclear', 'onfocus', 'contextmenu', 'save', 'input']);
const global = {};
const state = reactive({
	editorInstance: {} as monaco.editor.IStandaloneCodeEditor,
	defaultOptions: {
		theme: "vs-dark",
		fontSize: 14
	}
})

const getHints = model => {
  let id = model.id.substring(6);
  return (global[id] && global[id].hints) || [];
};
monaco.languages.registerCompletionItemProvider(
  "sql",
  createSqlCompleter(getHints)
);
monaco.languages.registerCompletionItemProvider(
  "javascript",
  createJavascriptCompleter(getHints)
);
registerLanguage(monaco);

watch(() => props.value, (val:string | undefined) => {
	if (val !== state.editorInstance.getValue()) {
		state.editorInstance.setValue(val);
	}
})
const onValueChange = () => {
	emit("input", state.editorInstance.getValue());
	emit("change", state.editorInstance.getValue());
}
const layout = () => {
	state.editorInstance.layout();
}
const undo = () => {
	state.editorInstance.trigger("anyString", "undo");
	onValueChange();
}
const redo = () => {
	state.editorInstance.trigger("anyString", "redo");
	onValueChange();
}
const getOptions = () => {
	let propsT = { 
		value: props.value,
		language: props.language
	}
	let options = Object.assign({}, state.defaultOptions, props.options, propsT);
	return options;
}
const initEditor = () => {
	(self as any).MonacoEnvironment = {
		getWorker(_: string, label: string) {
			if (label === 'json') {
				return new JsonWorker()
			}
			if (['css', 'scss', 'less'].includes(label)) {
				return new CssWorker()
			}
			if (['html', 'handlebars', 'razor'].includes(label)) {
				return new HtmlWorker()
			}
			if (['typescript', 'javascript'].includes(label)) {
				return new TsWorker()
			}
			return new EditorWorker()
		},
	};

	state.editorInstance = monaco.editor.create(
		editor,
		getOptions()
	);
	state.editorInstance.onContextMenu(e => {
		emit("contextmenu", e);
	});
	state.editorInstance.onDidChangeModelContent(() => {
		onValueChange();
	});
	state.editorInstance.addCommand(
		monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS,
		() => {
			emit("save", state.editorInstance.getValue());
		}
	);
}
// 页面加载时
onMounted(() => {
	initEditor();
	global[state.editorInstance._id] = this;
	window.addEventListener("resize", layout);
})
</script>

<style scoped>
.main :deep(.view-lines) * {
  font-family: Consolas, "Courier New", monospace !important;
}
</style>